package zom.zenjava.zendeploy;

import zom.zenjava.zendeploy.model.ApplicationProfile;
import zom.zenjava.zendeploy.model.CheckForUpdateResult;
import zom.zenjava.zendeploy.util.ApplicationProfileReader;
import zom.zenjava.zendeploy.util.FileDownloader;

import java.io.File;
import java.io.IOException;
import java.net.URL;

public abstract class ZenDeployManager
{
    private static ZenDeployManager instance;
    
    public static ZenDeployManager getInstance() throws ZenDeployException
    {
        if (instance == null)
        {
            String implClassName = System.getProperty("zenDeployManager");

            // default to windows
            if (implClassName == null)
            {
                implClassName = "com.zenjava.zendeploy.win.ZenDeployManagerWinImpl";
            }

            try
            {
                Class implClass = Class.forName(implClassName);
                instance = (ZenDeployManager)implClass.newInstance();
            }
            catch (ClassNotFoundException e)
            {
                throw new ZenDeployException(String.format(
                        "Unable to find zenDeployManager class on classpath: '%s'", implClassName), e);
            }
            catch (InstantiationException e)
            {
                throw new ZenDeployException(String.format(
                        "Unable to create an instance of zenDeployManager implementation: '%s'", implClassName), e);
            }
            catch (IllegalAccessException e)
            {
                throw new ZenDeployException(String.format(
                        "Unable to access zenDeployManager class: '%s'", implClassName), e);
            }
        }
        return instance;
    }

    public ApplicationProfile getInstalledApplicationProfile() throws ZenDeployException
    {
        ApplicationProfileReader reader = new ApplicationProfileReader();
        return reader.read(getInstalledApplicationProfileFile());
    }

    public CheckForUpdateResult checkForUpdatedApplicationProfile() throws ZenDeployException
    {
        try
        {
            ApplicationProfile currentAppProfile = getInstalledApplicationProfile();
            File tempFile = File.createTempFile("app-profile", ".xml");
            ApplicationProfile updatedAppProfile;
            try
            {
                FileDownloader downloader = new FileDownloader(new URL(currentAppProfile.getUrl()), tempFile);
                downloader.download();
                ApplicationProfileReader reader = new ApplicationProfileReader();
                updatedAppProfile = reader.read(tempFile);
                if (!currentAppProfile.getVersion().equals(updatedAppProfile.getVersion()))
                {
                    return new CheckForUpdateResult(currentAppProfile, updatedAppProfile);
                }
                else
                {
                    return new CheckForUpdateResult(currentAppProfile, null);
                }
            }
            finally
            {
                tempFile.delete();
            }
        }
        catch (IOException e)
        {
            throw new ZenDeployException("Error checking for updated application profile", e);
        }
    }

    public boolean upgradeToLatestApplicationProfile() throws ZenDeployException
    {
        try
        {
            ApplicationProfile currentAppProfile = getInstalledApplicationProfile();
            File tempFile = File.createTempFile("app-profile", ".xml");
            try
            {
                FileDownloader downloader = new FileDownloader(new URL(currentAppProfile.getUrl()), tempFile);
                downloader.download();
                ApplicationProfileReader reader = new ApplicationProfileReader();
                ApplicationProfile updatedAppProfile = reader.read(tempFile);
                if (!currentAppProfile.getVersion().equals(updatedAppProfile.getVersion()))
                {
                    return doUpgrade(getInstallationDirectory(), currentAppProfile, updatedAppProfile, tempFile);
                }
                else
                {
                    return false;
                }
            }
            finally
            {
                if (tempFile.exists())
                {
                    tempFile.delete();
                }
            }
        }
        catch (IOException e)
        {
            throw new ZenDeployException("Error checking for updated application profile", e);
        }
    }

    public abstract void restart() throws ZenDeployException;

    public File getInstallationDirectory()
    {
        String installDir = System.getProperty("zenInstallDir");
        return new File(installDir != null ? installDir : ".");
    }
    
    public File getInstalledApplicationProfileFile()
    {
        return new File(getInstallationDirectory(), "app-profile.xml");
    }

    protected abstract boolean doUpgrade(File installationDir,
                                         ApplicationProfile currentAppProfile,
                                         ApplicationProfile updatedAppProfile,
                                         File updatedAppProfileFile);
}
